using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using DependencyAnalysis.DataModel;

namespace DependencyAnalysis
{
    public static class VerifierUtilities
    {
        public static bool CompareToType(DependencyDescriptor dependency, IDescriptor referrer)
        {
            return Equals(dependency.Source.ImplementingType, referrer);
        }

        public static bool CompareToNamespace(DependencyDescriptor dependency, IDescriptor referrer)
        {
            return Equals(dependency.Source.ImplementingType.Submodule, referrer);
        }

        public static bool CompareToAssembly(DependencyDescriptor dependency, IDescriptor referrer)
        {
            return Equals(dependency.Source.ImplementingType.Submodule.Module, referrer);
        }

        public static bool CompareToMember(DependencyDescriptor dependency, IDescriptor referrer)
        {
            return Equals(dependency.Source, referrer);
        }

        public static void ThrowDependencyException(IEnumerable violations, string format)
        {
            var message = new StringBuilder();
            foreach (var violation in violations)
            {
                message.AppendLine(String.Format(format, violation));
            }
            throw new DependencyException(message.ToString());
        }

        public static void SelectTypes(IDependencyModel model, IDescriptor descriptor, ISet<TypeDescriptor> exceptionTypes)
        {
            var type = descriptor as TypeDescriptor;
            if (type != null)
            {
                var foundType = (from x in model.DependenciesPerType
                                 where x.Key.Identifier == type.Identifier
                                 select x.Key).FirstOrDefault();
                if (foundType != null) exceptionTypes.Add((TypeDescriptor)foundType);
                return;
            }
            var namespaceDescriptor = descriptor as SubmoduleDescriptor;
            if (namespaceDescriptor != null)
            {
                (from x in model.DependenciesPerType
                 where ((TypeDescriptor)x.Key).Submodule.Identifier == namespaceDescriptor.Identifier
                 select x.Key).ToList().ForEach(x => exceptionTypes.Add((TypeDescriptor)x));
                return;
            }
            var module = descriptor as ModuleDescriptor;
            if (module != null)
            {
                (from x in model.DependenciesPerType
                 where ((TypeDescriptor)x.Key).Submodule.Module.Identifier == module.Identifier
                 select x.Key).ToList().ForEach(x => exceptionTypes.Add((TypeDescriptor)x));
            }
        }

        public static void SelectMembers(IDependencyModel model, IDescriptor descriptor, ISet<MemberDescriptor> exceptionTypes)
        {
            var member = descriptor as MemberDescriptor;
            if (member != null)
            {
                (from x in model.DependenciesPerMember
                 where x.Key.Identifier == member.Identifier
                 select x.Key).ToList().ForEach(x => exceptionTypes.Add((MemberDescriptor)x));
                return;
            }
            var type = descriptor as TypeDescriptor;
            if (type != null)
            {
                (from x in model.DependenciesPerMember
                 where ((MemberDescriptor)x.Key).ImplementingType.Identifier == type.Identifier
                 select x.Key).ToList().ForEach(x => exceptionTypes.Add((MemberDescriptor)x));
                return;
            }
            var namespaceDescriptor = descriptor as SubmoduleDescriptor;
            if (namespaceDescriptor != null)
            {
                (from x in model.DependenciesPerMember
                 where ((MemberDescriptor)x.Key).ImplementingType.Submodule.Identifier == namespaceDescriptor.Identifier
                 select x.Key).ToList().ForEach(x => exceptionTypes.Add((MemberDescriptor)x));
                return;
            }
            var module = descriptor as ModuleDescriptor;
            if (module != null)
            {
                (from x in model.DependenciesPerMember
                 where ((MemberDescriptor)x.Key).ImplementingType.Submodule.Module.Identifier == module.Identifier
                 select x.Key).ToList().ForEach(x => exceptionTypes.Add((MemberDescriptor)x));
            }
        }

        public static IEnumerable<ModuleDescriptor> AggregateDependenciesToModules(IEnumerable<DependencyDescriptor> dependencies, bool selectSources)
        {
            var types = new HashSet<ModuleDescriptor>();
            foreach (var dependency in dependencies)
            {
                var descriptor = selectSources ? dependency.Source.ImplementingType.Submodule.Module : dependency.Target.ImplementingType.Submodule.Module;
                if (types.Contains(descriptor)) continue;
                types.Add(descriptor);
            }
            return types;
        }

        public static IEnumerable<SubmoduleDescriptor> AggregateDependenciesToSubmodules(IEnumerable<DependencyDescriptor> dependencies, bool selectSources)
        {
            var submodules = new HashSet<SubmoduleDescriptor>();
            foreach (var dependency in dependencies)
            {
                var descriptor = selectSources ? dependency.Source.ImplementingType.Submodule : dependency.Target.ImplementingType.Submodule;
                if (submodules.Contains(descriptor)) continue;
                submodules.Add(descriptor);
            }
            return submodules;
        }

        public static IEnumerable<TypeDescriptor> AggregateDependenciesToTypes(IEnumerable<DependencyDescriptor> dependencies, bool selectSources)
        {
            var types = new HashSet<TypeDescriptor>();
            foreach (var dependency in dependencies)
            {
                TypeDescriptor type = selectSources ? dependency.Source.ImplementingType : dependency.Target.ImplementingType;
                if (types.Contains(type)) continue;
                types.Add(type);
            }
            return types;
        }

        public static IEnumerable<KeyValuePair<IDescriptor, int>> AggregateDependenciesWithCount(IEnumerable<DependencyDescriptor> dependencies,
             Func<MemberDescriptor, IDescriptor> selector, bool selectSources)
        {
            var types = new Dictionary<IDescriptor, int>();
            foreach (var dependency in dependencies)
            {
                var member = selector(selectSources ? dependency.Source : dependency.Target);
                if (types.ContainsKey(member))
                {
                    types[member]++;
                    continue;
                }
                types.Add(member, 1);
            }
            return types;
        }

        public static IEnumerable<MemberDescriptor> AggregateDependenciesToMembers(IEnumerable<DependencyDescriptor> dependencies, bool selectSources)
        {
            var types = new HashSet<MemberDescriptor>();
            foreach (var dependency in dependencies)
            {
                var member = selectSources ? dependency.Source : dependency.Target;
                if (types.Contains(member)) continue;
                types.Add(member);
            }
            return types;
        }
    }
}